#ifndef __MATRIX_4X4_H__
#define __MATRIX_4X4_H__

#pragma once

#include "Vector4.h"
#include "Matrix3x3.h"

namespace Nezha
{

	class FOUNDATION_ENTRY Matrix4x4
	{
	public:
		Matrix4x4() {} // without initialized

		Matrix4x4(Real f00, Real f01, Real f02, Real f03,
			Real f10, Real f11, Real f12, Real f13,
			Real f20, Real f21, Real f22, Real f23,
			Real f30, Real f31, Real f32, Real f33);

		Matrix4x4(const Matrix4x4 &mat)
		{
			memcpy(mEntries, mat.mEntries, sizeof(mEntries));
		}

		void makeZero()
		{
			memset(mEntries, 0, sizeof(mEntries));
		}

		void makeIdentity()
		{
			mEntries[0] = 1.0;
			mEntries[1] = 0.0;
			mEntries[2] = 0.0;
			mEntries[3] = 0.0;
			mEntries[4] = 0.0;
			mEntries[5] = 1.0;
			mEntries[6] = 0.0;
			mEntries[7] = 0.0;
			mEntries[8] = 0.0;
			mEntries[9] = 0.0;
			mEntries[10] = 1.0;
			mEntries[11] = 0.0;
			mEntries[12] = 0.0;
			mEntries[13] = 0.0;
			mEntries[14] = 0.0;
			mEntries[15] = 1.0;
		}

		const Real* operator[] (u32 iRow) const
		{
			return &mEntries[4*iRow];
		}

		Real* operator[] (u32 iRow)
		{
			return &mEntries[4*iRow];
		}

		Real operator() (u32 iRow, u32 iCol) const
		{
			return mEntries[iRow*4 + iCol];
		}

		Real& operator() (u32 iRow, u32 iCol)
		{
			return mEntries[iRow*4 + iCol];
		}

		Matrix4x4& operator = (const Matrix4x4 &mat)
		{
			memcpy(mEntries, mat.mEntries, sizeof(mEntries));
			return *this;
		}

		void setCol(u32 iCol, const Vector4 &col)
		{
			NZ_ASSERT(iCol < 4);
			mEntries[iCol] = col.x;
			mEntries[4 + iCol] = col.y;
			mEntries[8 + iCol] = col.z;
			mEntries[12 + iCol] = col.w;
		}

		void setCol(u32 iCol, Real x, Real y, Real z, Real w)
		{
			NZ_ASSERT(iCol < 4);
			mEntries[iCol] = x;
			mEntries[4 + iCol] = y;
			mEntries[8 + iCol] = z;
			mEntries[12 + iCol] = w;
		}

		void setCol(u32 iCol, Real* pArray)
		{
			NZ_ASSERT(iCol < 4);
			mEntries[iCol] = pArray[0];
			mEntries[4 + iCol] = pArray[1];
			mEntries[8 + iCol] = pArray[2];
			mEntries[12 + iCol] = pArray[3];			
		}

		void getCol(u32 iCol, Vector4& v) const
		{
			NZ_ASSERT(iCol < 4);
			v.x = mEntries[iCol];
			v.y = mEntries[4 + iCol];
			v.z = mEntries[8 + iCol];
			v.w = mEntries[12 + iCol];
		}

		void getCol(u32 iCol, Real& x, Real& y, Real& z, Real& w)
		{
			NZ_ASSERT(iCol < 4);
			x = mEntries[iCol];
			y = mEntries[4 + iCol];
			z = mEntries[8 + iCol];
			w = mEntries[12 + iCol];
		}

		void getCol(u32 iCol, Real* pArray)
		{
			NZ_ASSERT(iCol < 4);
			pArray[0] = mEntries[iCol];
			pArray[1] = mEntries[4 + iCol];
			pArray[2] = mEntries[8 + iCol];
			pArray[3] = mEntries[12 + iCol];
		}

		void getRow(u32 iRow, Vector4 &row) const
		{
			NZ_ASSERT(iRow < 4);
			int i = iRow << 2;
			row.x = mEntries[i];
			row.y = mEntries[++i];
			row.z = mEntries[++i];
			row.w = mEntries[++i];
		}

		void getRow(u32 iRow, Real* pArray) const
		{
			NZ_ASSERT(iRow < 4);
			int i = iRow << 2;
			pArray[0] = mEntries[i];
			pArray[1] = mEntries[++i];
			pArray[2] = mEntries[++i];
			pArray[3] = mEntries[++i];
		}

		void setRow(u32 iRow, const Vector4 &row)
		{
			NZ_ASSERT(iRow < 4);
			int i = iRow << 2;
			mEntries[i] = row.x;
			mEntries[++i] = row.y;
			mEntries[++i] = row.z;
			mEntries[++i] = row.w;
		}

		void setRow(u32 iRow, Real x, Real y, Real z, Real w)
		{
			NZ_ASSERT(iRow < 4);
			int i = iRow << 2;
			mEntries[i] = x;
			mEntries[++i] = y;
			mEntries[++i] = z;
			mEntries[++i] = w;
		}

		void setRow(u32 iRow, Real* pArray)
		{
			NZ_ASSERT(iRow < 4);
			int i = iRow << 2;
			mEntries[i] = pArray[0];
			mEntries[++i] = pArray[1];
			mEntries[++i] = pArray[2];
			mEntries[++i] = pArray[3];
		}

		Matrix4x4 operator+ (const Matrix4x4 &mat) const;
		Matrix4x4 operator- (const Matrix4x4 &mat) const;
		Matrix4x4 operator* (const Matrix4x4 &mat) const;
		Matrix4x4 operator* (Real fScalar) const;
		Matrix4x4 operator/ (Real fScalar) const;
		Matrix4x4 operator- () const;

		Matrix4x4& operator+= (const Matrix4x4 &mat);
		Matrix4x4& operator-= (const Matrix4x4 &mat);
		Matrix4x4& operator*= (Real fScalar);
		Matrix4x4& operator/= (Real fScalar);

		Vector4 operator* (const Vector4 &v) const;

		Matrix4x4 transpose() const;
		Matrix4x4 transposeTimes(const Matrix4x4 &mat) const; // this~T * M
		Matrix4x4 timesDiagonal(const Vector4&diag) const;
		Matrix4x4 inverse() const;
		Real determiant() const;

	private:
		Real mEntries[16];

	public:
		static const Matrix4x4 ZERO;
		static const Matrix4x4 IDENTITY;
	};

}//end namespace Nezha

#endif //end __MATRIX_4X4_H__